package com.lipaluma.field.validator.impl.numeric;

import java.lang.reflect.Field;
import java.util.List;

import com.lipaluma.annotations.validations.ValidateInteger;
import com.lipaluma.context.Context;
import com.lipaluma.field.validator.ValidationError;
import com.lipaluma.field.validator.Validator;
import com.lipaluma.xml.validator.XmlMinMaxValidator;
import com.lipaluma.xml.validator.XmlValidator;

/**
 * Validate a numeric integer field to check if a value of the field is greater, less than a given value.
 * Or more, validate directly a range of min and max values.<br/>
 * <p>
 * To initialize :
 * <ul>
 * 	<li>
 * 	<u><b>By annotations :</b></u> Use {@link ValidateInteger} and use the <code>min</code> attribute to 
 * specify a min value and the <code>max</code> attribute to specify the max.
 * 	</li>
 * 	<li>
 * 	<u><b>By Xml :</b></u>
 * <ul>
 * <li>
 * To add a min or max validation (or both), see the example below : <br/>
 * <code>
 * &lt;ToMap field="..."   ... min="5" max="9"/><br/>
 * </code>
 * This example specify a min and max value. A min or max value can be specified alone.
 * </li>
 * </ul>
 * 
 * @author Mario<br/>
 * <br/>
 * <b>The code in inspired by : </b> 
 * @author Kasper B. Graversen from super-csv
 * @author James Bassett from super-csv
**/
public class LMinMaxValidator implements Validator {

	private Long min = Long.MIN_VALUE;
	private Long max = Long.MAX_VALUE;
	
	public LMinMaxValidator() {}
	public LMinMaxValidator(Long min, Long max) {
		this.min = (min != null)? min : Long.MIN_VALUE;
		this.max = (max != null)? max : Long.MAX_VALUE;
		checkPreconditions(this.min, this.max);
	}
	public LMinMaxValidator(Integer min, Integer max) {
		this.min = (min != null)? new Long(min) : Long.MIN_VALUE;
		this.max = (max != null)? new Long(max) : Long.MAX_VALUE;
		checkPreconditions(this.min, this.max);
	}
	
	public boolean init(Context context, Field field) {
		ValidateInteger annotation = field.getAnnotation(ValidateInteger.class);
		if(annotation == null || (annotation.min() == Long.MIN_VALUE && annotation.max() == Long.MAX_VALUE))
			return false;
		min = annotation.min();
		max = annotation.max();
		checkPreconditions(min, max);
		return true;
	}
	
	public boolean init(Context context, Field field, XmlValidator xmlValidator) {
		XmlMinMaxValidator validator = (XmlMinMaxValidator) xmlValidator;
		if(validator.getMinValueAsLong() != null)
			this.min = validator.getMinValueAsLong();
		if(validator.getMaxValueAsLong() != null)
			this.max = validator.getMaxValueAsLong();
		checkPreconditions(this.min, this.max);
		return true;
	}
	
	private void checkPreconditions(long min, long max) {
		if(min > max)
			throw new IllegalArgumentException(String.format("max (%d) should not be < min (%d)", max, min));
	}
	
	public boolean validate(Object value, List<ValidationError> errors) {
		Long result = new Long(value.toString());
		if(min != null && result < min) {
			errors.add(new ValidationError("The value "+result+" is strictly lower than "+min));
			return false;
		}
		if(max != null && result > max ) {
			errors.add(new ValidationError("The value "+result+" is strictly greater than "+max));
			return false;
		}
		return true;
	}
}
